package com.griddroid.works;

import java.io.Serializable;
import com.griddroid.computations.Computation;
import com.griddroid.computations.Integral;
import com.griddroid.computations.Product;
import com.griddroid.computations.Sum;
import com.griddroid.exceptions.UnknownComputationException;

/**
 * Represents a request from the client to the Grid. It contains a
 * {@link Computation Computation}, which is in fact the actual work that the
 * client requested. The clients sends this work request firstly to the server,
 * which is responsible for splitting it and sending it to the workers to get
 * executed
 * 
 * @author Charalampos Mavroforakis
 * @author Thomas Ailianos
 * @author Michail Michailidis
 * 
 */
public class WorkRequest implements Serializable
{

	private static final long serialVersionUID = 7015286400471068139L;
	private Computation comp;
	private String workRequestId;
	private int workFragmentNum;
	private int totalFragments;

	/**
	 * Constructs a work request. It also sets <code>
	 * this.workFragmentNum = 1;
	 * this.totalFragments = 1;
	 * </code> meaning that it is a solid work request (1 out of 1)
	 * 
	 * @param computation
	 *            the actual work of this work request
	 * @param workRequestId
	 *            a unique identifier of this work request. It is the id of the
	 *            client that sent this work request
	 */
	public WorkRequest(Computation computation, String workRequestId)
	{
		this.comp = computation;
		this.workRequestId = workRequestId;
		this.workFragmentNum = 1;
		this.totalFragments = 1;
	}

	/**
	 * This constructor is used when splitting a solid work request into small
	 * ones. That's why we can set now which fragment this one is and how many
	 * fragments there are in total
	 * 
	 * @param computation
	 *            the actual work of this work request
	 * @param workRequestId
	 *            a unique identifier of this work request. It is the id of the
	 *            client that sent this work request
	 * @param frgId
	 *            the fragment ascending number.
	 * @param number_of_frags
	 *            the total number of fragments that the original work request
	 *            was split into.
	 */
	public WorkRequest(Computation computation, String workRequestId, int frgId, int number_of_frags)
	{
		this.comp = computation;
		this.workRequestId = workRequestId;
		this.workFragmentNum = frgId;
		this.totalFragments = number_of_frags;
	}

	/**
	 * Splits the work request in a given number of parts. There is no point in
	 * making a new class for the fragments, as they would be exactly the same
	 * as this one
	 * 
	 * @param parts
	 *            the number of parts that the work request will be split into
	 * @return an array with all the small pieces that the original work request
	 *         was split
	 */
	public WorkRequest[] split(int parts)
	{
		Computation[] computations = comp.split(parts);
		WorkRequest[] wrs = new WorkRequest[computations.length];
		int i = 0;
		for (Computation c : computations)
		{
			wrs[i] = new WorkRequest(c, this.workRequestId, i, computations.length);
			i++;
		}
		return wrs;
	}

	/**
	 * Gets the computation included in the work request
	 * 
	 * @return the computation of the work request
	 */
	public Computation getComp()
	{
		return this.comp;
	}

	/**
	 * Carries out the computation of the work request, returning a work
	 * response. If you decide to extend the functionality of the Grid, you
	 * should add your code here, to handle your own Computation
	 * 
	 * @return a work response that has the result of the computation and
	 *         information about to which work request (and fragment) it belongs
	 * @throws UnknownComputationException
	 *             there is no information on how to process (
	 * @see WorkResponse
	 */
	public WorkResponse process() throws UnknownComputationException
	{
		if (this.comp instanceof Integral)
		{
			((Integral) (this.comp)).calculate();
			return new WorkResponse(((Integral) this.comp), workRequestId, this.workFragmentNum, this.totalFragments);
		}
		else if (this.comp instanceof Sum)
		{
			((Sum) (this.comp)).calculate();
			return new WorkResponse(((Sum) this.comp), workRequestId, this.workFragmentNum, this.totalFragments);
		}
		else if (this.comp instanceof Product)
		{
			((Product) (this.comp)).calculate();
			return new WorkResponse(((Product) this.comp), workRequestId, this.workFragmentNum, this.totalFragments);
		}
		else
			throw new UnknownComputationException();

	}

	public String getWorkRequestId()
	{
		return workRequestId;
	}

	public String getWorkFragmentId()
	{
		return "" + String.valueOf(this.totalFragments) + "@" + String.valueOf(this.workFragmentNum);
	}

	public int getWorkFragmentNum()
	{
		return workFragmentNum;
	}

	public int getTotalFragments()
	{
		return totalFragments;
	}

	public void setWorkRequestId(String id)
	{
		this.workRequestId = id;
	}

}
